﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;
using SterlingQuoteCollector.Properties;
using System.ComponentModel;
using Algobox.Apps.Finance.SterlingQuoteRecorder;
using Algobox.Toolbox.Diagnostics;

namespace SterlingQuoteCollector
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            ASSEMBLY_NAME = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;

            Log.Info(".......");
            Log.Info(":::::::");
            Log.Info("Loading {0}, {1} ", ASSEMBLY_NAME, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString());

            Log.Info("Attempting to collect from {0} for user {1}", Settings.Default.SterlingExecutable, Settings.Default.Username);

            _counterInitialLaunchAttempts = 0;
            
            _emailUser = Settings.Default.EmailUsername;
            _emailRecipient = Settings.Default.EmailRecipient;
            _emailer = new Algobox.Toolbox.Net.Mail.GmailClient(_emailUser, Settings.Default.EmailPassword);

            while (_counterInitialLaunchAttempts++ < MAX_ATTEMPTS)
            {
                if (TryLaunchSterling())
                {
                    Log.Info("Successfuly attached to processid {0} with a start time of {1}", _processSterling.Id, _processSterling.StartTime);
                    _processSterling.EnableRaisingEvents = true;
                    _processSterling.Exited += new EventHandler(OnProcessExited);
                    
                    BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
                    worker.DoWork += new DoWorkEventHandler(LaunchApp);
                    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
                    worker.RunWorkerAsync(Settings.Default.AlgoboxConnection);
                                        
                    return;
                }
            }

            SendEmail("Failed to attach or launch Sterling process");

            Log.Info("Failed to attach or launch Sterling process for {0}", Settings.Default.SterlingExecutable);
            Log.Info("{0} Terminated", ASSEMBLY_NAME);
            Log.Info(":::::::");
            Log.Info(".......");
            Close();
        }

        const int MAX_ATTEMPTS = 3;
        private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();
        Process _processSterling;
        int _counterInitialLaunchAttempts;

        readonly string ASSEMBLY_NAME;

        Algobox.Toolbox.Net.Mail.GmailClient _emailer;
        string _emailUser;
        string _emailRecipient;

        SterlingQuoteRecorder _app;

        public delegate void DelegateDispatchClose();

        void DispatchClose()
        {
            Close();
        }

        void SendEmail(string message)
        {
            _emailer.SendMail(_emailUser, _emailRecipient, ASSEMBLY_NAME, message);
        }

        void SendEmail(string messageFormat, params object[] args)
        {
            _emailer.SendMail(_emailUser, _emailRecipient, ASSEMBLY_NAME, String.Format(messageFormat, args));
        }

        /// <summary>
        /// Attempts to find or start the sterling pro process
        /// </summary>
        /// <returns></returns>
        private bool TryLaunchSterling()
        {
            string processFilename = Settings.Default.SterlingExecutable;

            string appFilename = System.Reflection.Assembly.GetExecutingAssembly().Location;

            foreach (string dump in System.IO.Directory.EnumerateFiles(System.IO.Path.GetDirectoryName(appFilename), "*.dmp", System.IO.SearchOption.TopDirectoryOnly))
            {
                System.IO.File.Delete(dump);
            }

            Process[] processList = Process.GetProcesses();

            //_processSterling = processList.Where(a => a.MainModule is ProcessModule).FirstOrDefault(a => a.MainModule.FileName == filename);
            foreach (Process proc in processList)
            {
                try
                {
                    if (proc.MainModule is ProcessModule && proc.MainModule != null)
                    {
                        Debug.WriteLine("Proc: " + proc.MainModule.ModuleName);
                        if (proc.MainModule.FileName == processFilename)
                        {
                            _processSterling = proc;
                        }
                    }
                }
                catch { }
            }
            
            if (_processSterling != default(Process))
            {
                _processSterling.Kill();
                _processSterling.Dispose();
                _processSterling = null;
            }

            try
            {
                string args = String.Format("/pid:{0} /password:{1} /ServerIP:{2} /ServerPort:{3} /QuoteServerIP:{4} /QuoteServerPort:{5} /ServerParams", 
                    Settings.Default.Username, 
                    Settings.Default.Password,
                    Settings.Default.ServerIP,
                    Settings.Default.ServerPort,
                    Settings.Default.QuoteIP,
                    Settings.Default.QuotePort);

                ProcessStartInfo info = new ProcessStartInfo(processFilename, args);  
                _processSterling = Process.Start(info);
                if (!_processSterling.HasExited)
                    return true;
            }
            catch (Exception e)
            {
                Log.Error("Failed to start Sterling process on attempt {2} File({0}) Exception({1})", processFilename, e.Message, _counterInitialLaunchAttempts);
            }

            return false;
        }

        /// <summary>
        /// Main window shutdown
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnWindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (_app != null)
            {
                _app.Dispose();
            }

            if (_processSterling != null)
            {
                _processSterling.Kill();
                _processSterling.Dispose();
            }
            Log.Info("{0} Terminated", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
            Log.Info(":::::::");
            Log.Info(".......");
        }

        void OnProcessExited(object sender, EventArgs e)
        {
            Log.Info("Sterling process has exited with code {0}", _processSterling.ExitCode);
            _app.Dispose();
            Log.Info("{0} Terminated", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
            Log.Info(":::::::");
            Log.Info(".......");
            this.Dispatcher.BeginInvoke(new DelegateDispatchClose(DispatchClose), null);
        }
        
        private void LaunchApp(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            if (_app == null)
            {
                _app = new SterlingQuoteRecorder((string)e.Argument);
                _app.OnShutdown += new Action<string>(_app_OnShutdown);
            }
            else
            {
                _app.PriceFeed.OnMarketFeedShutdown += new Action(PriceFeed_OnMarketFeedShutdown);
                _app.Start();
            }
        }

        void _app_OnShutdown(string message)
        {
            SendEmail("Application is shuttinf down\n" + message);
            Log.Info("Application has announced shutdown");
            _app.Dispose();
            Log.Info("{0} Terminated", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
            Log.Info(":::::::");
            Log.Info(".......");
            this.Dispatcher.BeginInvoke(new DelegateDispatchClose(DispatchClose), null);
        }

        void PriceFeed_OnMarketFeedShutdown()
        {
            Log.Info("Detected Sterling Msarket Feed shutdown");
            _app.Dispose();
            Log.Info("{0} Terminated", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
            Log.Info(":::::::");
            Log.Info(".......");
            this.Dispatcher.BeginInvoke(new DelegateDispatchClose(DispatchClose), null);
        }

        void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Binding bind;

            bind = new Binding("PricesPerSecond");
            bind.Source = _app;
            _labelMsgPerSecLast.SetBinding(TextBlock.TextProperty, bind);
            bind = new Binding("UpdateTimestamp");
            bind.Source = _app;
            _labelLastTimestamp.SetBinding(TextBlock.TextProperty, bind);
            bind = new Binding("NumberOfRequests");
            bind.Source = _app;
            _labelNumRequests.SetBinding(TextBlock.TextProperty, bind);
            bind = new Binding("TotalUpdates");
            bind.Source = _app;
            _labelTotalUpdates.SetBinding(TextBlock.TextProperty, bind);
            bind = new Binding("UpdateTimestamp");
            bind.Source = _app;
            _labelLastTimestamp.SetBinding(TextBlock.TextProperty, bind);
            bind = new Binding("UpdatesCollected");
            bind.Source = _app;
            _labelCollectionStat.SetBinding(TextBlock.TextProperty, bind);
            bind = new Binding("UpdatesWritten");
            bind.Source = _app;
            _labelWriteStat.SetBinding(TextBlock.TextProperty, bind);
            bind = new Binding("FailureMessage");
            bind.Source = _app;
            _labelWriteFailure.SetBinding(TextBlock.TextProperty, bind);

            BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(LaunchApp);
            worker.RunWorkerAsync();
        }

        

    }
}
